#
#	coding:utf-8

import socket
import sys
import array    # C interface
import time
import struct
import threading

def print_array(arr):
    length = len(arr)
    for i in range(length):
        print('%02X '%(arr[i]), end='')
        if (i%16)==7:
            print('-', end='')
        if (i%16)==15:
            print('')
    print('')

# 定义了各个不同的字节所代表的含义

SSIMP_SRC_TYPE = 0
SSIMP_SRC_ID   = 1
SSIMP_DST_TYPE = 2
SSIMP_DST_ID   = 3
SSIMP_SERIAL   = 4
SSIMP_PKG_NUM  = 5
SSIMP_PKG_LENH = 6
SSIMP_PKG_LENL = 7 


SSIMP_CMDO_PARAM = 1
SSIMP_CMDO_LENH = 2
SSIMP_CMDO_LENL = 3
SSIIMP_CMDO_DATA = 4

SSIMP_SERVICE_CMD	=8		# SIMP service 命令
SSIMP_SERVICE_PARAM	=9		# SIMP service 参数
SSIMP_SERVICE_LENH	=10		# SIMP service 参数
SSIMP_SERVICE_LENL	=11		# SIMP service 参数

SSIMP_SERVICE_DATA	=12		# SIMP service 数据

SSIMP_CANO_PORT = 0
SSIMP_CANO_LEN = 1
SSIMP_CANO_ID = 2
SSIMP_CANO_DATA = 6

# 定义了设备类型
MDL_SCHDULER = 0x01 # /**< 调度器 */
MDL_TRAIN    = 0x10 # /**< 列车模型 */
MDL_LINE     = 0x11 # /**< 线路模型 */
MDL_OBUI     = 0x12 # /**< 车载UI */
MDL_WSUI     = 0x13 # /**< 站场图UI */

MDL_OBATP    = 0x40 # /**< ATP */
MDL_OBATO    = 0x41 # /**< ATO */
MDL_OBBTM    = 0x42 # /**< BTM */
MDL_OBHMI    = 0x43 # /**< HMI */
MDL_OBGPS    = 0x44 # /**< GPS */
MDL_OBRADAR  = 0x45 # /**< RADAR */

MDL_WSCI     = 0x80 # /**< CI  */
MDL_WSZC     = 0x81 # /**< ZC  */
MDL_WSATS    = 0x82 # /**< ATS */
MDL_WSMSS    = 0x83 # /**< MSS */

MDL_BRDCST   = 0xff # /**< 广播 */

# 媒介类型
DTYPE_CMD   	= 0x01 # /**< 命令信息# 参照服务表 */
DTYPE_CMDSTR   	= 0x02 # /**< 命令字符串信息 */
DTYPE_DI        = 0x10 # /**< 数字输入 */
DTYPE_DO     	= 0x20 # /**< 数字输出 */
DTYPE_PULSE 	= 0x30 # /**< 脉冲点 */
DTYPE_CAN       = 0x40 # /**< CAN */
DTYPE_UART      = 0x50 # /**< 串口 */
DTYPE_SPI       = 0x60 # /**< SPI总线 */
DTYPE_LCAN      = 0x70 # /**< 长包CAN */
DTYPE_CURVE     = 0x80 # /**< 绘制曲线*/
DTYPE_BALISE    = 0x90 # /**< 应答器信息*/
DTYPE_UDP       = 0xA0 # /**< 外部的UDP信息*/
DTYPE_TCP       = 0xB0 # /**< 外部的TCP信息*/

CMD_START    = 0x01 # /**< 模型启动 */
CMD_PAUSE    = 0x02 # /**< 模型暂停 */
CMD_RESUME   = 0x03 # /**< 模型继续 */
CMD_EXIT     = 0x04 # /**< 模型退出 */
CMD_REGISTER = 0x05 # /**< 模型注册 */
CMD_SYNC     = 0x06 # /**< 模型同步 */
CMD_CFGFILE  = 0x10 # /**< 模型配置文件 */

def ssimp_recv_hook(sp):
    ##
    # @brief 提供接收udp线程
    s = sp.rx_sock
    while sp.run:
        try:  
            s.settimeout(1)  
            d,a=s.recvfrom(8192)
            sp.recv_handler(d)
        except socket.timeout:  
            pass

class ssimp_base:
    def __init__(self, src_type, src_id, rx_port, tx_addr="127.0.0.1", tx_port=8010):
        """
         @brief __init__ 
        
         @param src_type 源数据
         @param src_id  源编号
         @param rx_port 接收端口
         @param tx_addr 发送地址
         @param tx_port 发送端口
        
        """
        self.rx_sock = None 
        self.rx_port = rx_port
        self.rx_thread = None
        self.run = False
        self.tx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.tx_addr = (tx_addr, tx_port)
        self.serial = 0
        self.tx_data = [0]*512
        self.tx_data[SSIMP_PKG_NUM] = 1
        self.tx_data[SSIMP_SRC_TYPE] = src_type
        self.tx_data[SSIMP_SRC_ID] = src_id

    def data_parser(self, cmd, param, data):
        """
         @brief parser 扩展数据解释器，可重载
         @param cmd  命令
         @param param 命令参数
         @param data 命令数据
        """
        if cmd == DTYPE_DI:
            print("Get DI [%d]:"%len(data))
            print_array(data)
    
    def parser(self, cmd, param, data):
        """
         @brief parser 数据解释器
         @param cmd  命令
         @param param 命令参数
         @param data 命令数据
        """
        if cmd == DTYPE_CMDSTR:
            print("Get %s", data)
        elif cmd == DTYPE_CMD:
            print("Get CMD")
        else:
            self.data_parser(cmd, param, data)

    def launch_recv(self, rx_port=0):
        """
         @brief launch_recv 启动接收线程
         @param rx_port 接收端口，0表示使用默认，其他值表示启动
        """
        if rx_port != 0:
            self.rx_port = rx_port
        self.rx_sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)  
        self.rx_sock.bind(("", self.rx_port))
        self.rx_thread = threading.Thread(target=ssimp_recv_hook, args=(self, ))
        self.run = True
        self.rx_thread.setDaemon(True)
        self.rx_thread.start()
    
    def close(self):
        self.run = False

    def recv_handler(self, buff):
        """
         @brief ssimp的接收处理函数，可直接使用，或通过lanch自己启动线程调用
         @param 从udp或其他端口接收到的数据
        """
        pack_num = buff[SSIMP_PKG_NUM]
        pack_len = buff[SSIMP_PKG_LENH]*256+buff[SSIMP_PKG_LENL]

        if pack_num > 10:
            pack_num = 10

        base = SSIMP_SERVICE_CMD
        while pack_num > 0:
            cmd = buff[base ]
            param = buff[base + SSIMP_CMDO_PARAM]
            service_len =  buff[base+SSIMP_CMDO_LENH]*256 + buff[base+SSIMP_CMDO_LENL]
            self.parser(cmd, param, buff[base+SSIIMP_CMDO_DATA:base+SSIIMP_CMDO_DATA+service_len])
            pack_num = pack_num - 1
            base = base + service_len

    def raw_send(self, buff, buff_len):
        """
         @brief 发送buff内容
         @param buff 数据区
         @param buff_len 数据长度
        """
        self.serial = self.serial + 1
        dlen = int(int((buff_len+3)/4)*4);

        print(dlen, len(buff))

        # 复制数据，并按照4字节填充
        for i in range(dlen):
            if i >= buff_len:
                self.tx_data[SSIMP_SERVICE_DATA+i] = 0
            else:
                self.tx_data[SSIMP_SERVICE_DATA+i] = buff[i]

        # 更新service长度
        self.tx_data[SSIMP_SERVICE_LENH] = dlen//256
        self.tx_data[SSIMP_SERVICE_LENL] = dlen%256

        # 更新package长度
        dlen = dlen + SSIMP_SERVICE_DATA - SSIMP_SERVICE_CMD # + 4
        self.tx_data[SSIMP_PKG_LENH] = dlen//256
        self.tx_data[SSIMP_PKG_LENL] = dlen%256

        dlen = dlen + SSIMP_SERVICE_CMD

        # 更新序列号
        self.tx_data[SSIMP_SERIAL] = self.serial%256

        b = array.array('B', self.tx_data[0:dlen])
        self.tx_sock.sendto(b, 0, self.tx_addr)

    def cmd_send(self, cmd, param, buff, buff_len):
        """
         @brief cmd_send 提供CAN发送内容
         @port 端口号
         @id CAN发送ID
         @data CAN数据包
        """
        self.tx_data[SSIMP_SERVICE_CMD] = cmd
        self.tx_data[SSIMP_SERVICE_PARAM] = param
        self.raw_send(buff, buff_len)

    def can_send(self, port, id, data):
        """
         @brief can_send 提供CAN发送内容
         @port 端口号
         @id CAN发送ID, uint32_t
         @data CAN数据包
        """
        plen = len(data)+6
        buff = [0]*8
        buff[SSIMP_CANO_PORT] = port
        buff[SSIMP_CANO_LEN] = plen
        # 将id移位后存入buff
        for i in range(4):
            tmp = id&0x000000ff;
            buff[SSIMP_CANO_ID+3-i] = tmp
            id = id >> 8
        for i in range(plen-SSIMP_CANO_DATA):
            buff[SSIMP_CANO_DATA+i] = data[i]
        return self.cmd_send(DTYPE_CAN, port, buff)

if __name__ == '__main__':
    rx_port = 8010
    tx_port = 8002
    if (len(sys.argv) > 1):
        rx_port = int(sys.argv[1])
        if (len(sys.argv) > 2):
            tx_port = int(sys.argv[2])

    s = ssimp_base(MDL_LINE, 1, rx_port, '127.0.0.1', tx_port)
    s.launch_recv()
    print("HElP")
    di_data = [0x11, 0x22, 0x33]

    try:
        for i in range(5):
            s.cmd_send(DTYPE_DI, 0, di_data, len(di_data))
            time.sleep(1)
    except KeyboardInterrupt as k:
        s.close()
